// SPDX-License-Identifier: GPL-2.0
/*
 * sgm41620.c
 *
 * sgm41620 driver
 *
 * Copyright (c) 2021-2021 Huawei Technologies Co., Ltd.
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
 * may be copied, distributed, and modified under those terms.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 */
#include "sgmdev_charger.h"
#include "insdrv_i2c.h"
#undef LOG_TAG
#define LOG_TAG "sgm_charger"
#include "elog.h"

#if CHIP_TYPE == CHIP_SG41620

#define SGM41620_CHARGER_DEV_ADDR  (0x51  << 1)
#define SGM41620_CHARGER_TRIM_ADDR  (0x54  << 1)

#define SGM41620_ACDRV_EN       BIT(5)
#define SGM41620_ACDRV_MANUAL_EN BIT(4)
#define SGM41620_HIZ_EN         BIT(7)
#define SGM41620_CHRG_EN        BIT(0)
#define SGM41620_QB_EN          BIT(2)
#define SGM41620_BOOST_EN       BIT(1)

#define SGM41620_VSYS_MIN    0x30
#define SGM41620_VBAT_REG    0x31
#define SGM41620_ICHG_CC     0x32

#define SGM41620_QB_EN      BIT(2)
#define SGM41620_BOOST_EN   BIT(1)
#define SGM41620_CHG_EN     BIT(0)

enum SGM41606_BIT_OPTION {
	/* REG 0x30 ~ 0x34*/	
	B_VSYS_MIN,
	B_BATSNS_EN,B_VBAT_REG,
	B_ICHG_CC,
	B_VINDPM_VBAT,B_VINDPM_DIS,B_VINDPM	,
	B_IINDPM_DIS,B_IINDPM,
	/* REG 35*/	
	B_FORCE_ICO,B_ICO_EN,B_IINDPM_ICO,
	
	/* REG 36~3B*/
	B_VBAT_PRECHG,B_IPRECHG,
	B_TERM_EN,B_ITERM,
	B_RECHG_DIS,RECHG_DG,VRECHG,
	B_VBOOST,B_IBOOST_LIM,
	B_TSBAT_JEITA_DIS,B_IBAT_OCP_DIS,B_VPMID_OVP_OTG_DIS,B_VBAT_OVP_BUCK_DIS,
	B_T_BATFET_RST,B_T_PD_nRST,B_BATFET_RST_EN,BATFET_DLY,BATFET_DIS,nRST_DIS,
	/* REG 3C*/
	B_HIZ_EN,B_PERFORMANCE_EN,B_DIS_BUCKCHG_PATH,B_DIS_SLEEP_FOR_OTG,B_QB_EN,B_BOOST_EN,B_CHG_EN,
	/* REG 3D*/
	B_IBATOCP,B_VSYSOVP_DIS,B_VSYSOVP_TH,
	
	/* REG 3F ~0x46*/
	B_TMR2X_EN,B_CHG_TIMER_EN,B_CHG_TIMER,B_TDIE_REG_DIS,B_TDIE_REG,B_PFM_DIS,	
	B_BOOST_FREQ,B_BUCK_FREQ,B_BAT_LOAD_EN,
	B_VSYS_SHORT_STAT,B_VSLEEP_BUCK_STAT,B_VBAT_DPL_STAT,B_VBAT_LOW_BOOST_STAT,B_VBUS_GOOD_STAT,	
	B_CHG_STAT,B_BOOST_OK_STAT,B_VSYSMIN_REG_STAT,B_QB_ON_STAT,
	B_TDIE_REG_STAT,B_TSBAT_COOL_STAT,B_TSBAT_WARM_STAT,B_ICO_STAT,B_VSYS_STAT,B_IINDPM_STAT,B_VINDPM_STAT,
	B_VSYS_SHORT_FLG,B_VSLEEP_BUCK_FLG,B_VBAT_DPL_FLG,B_VBAT_LOW_BOOST_FLG,B_VBUS_GOOD_FLG,
	B_CHG_FLG,B_BOOST_OK_FLG,B_VSYSMIN_REG_FLG,B_QB_ON_FLG,
	B_TDIE_REG_FLG,B_TSBAT_COOL_FLG,B_TSBAT_WARM_FLG,B_ICO_FLG,B_IINDPM_FLG,B_VINDPM_FLG,
	
	/* REG 47*/
    B_VSYS_SHORT_MASK,B_VSLEEP_BUCK_MASK,B_VBAT_DPL_MASK,B_VBAT_LOW_BOOST_MASK,B_VBUS_GOOD_MASK,
	/* REG 48*/
	B_CHG_MASK,B_BOOST_OK_MASK,B_VSYSMIN_REG_MASK,B_QB_ON_MASK,
	B_TDIE_REG_MASK,B_TSBAT_COOL_MASK,B_TSBAT_WARM_MASK,B_ICO_MASK,B_IINDPM_MASK,B_VINDPM_MASK,
	/* REG 50*/
	B_CONV_OCP_STAT,B_VSYS_OVP_STAT,B_IBAT_OCP_STAT,B_VBAT_OVP_BUCK_STAT,
	
	B_OTG_HICCUP_STAT,B_CHG_TIMEOUT_STAT,B_VPMID_SHORT_STAT,B_VPMID_OVP_OTG_STAT,
	/* REG 52*/
	B_CONV_OCP_FLG,B_VSYS_OVP_FLG,B_IBAT_OCP_FLG,B_VBAT_OVP_BUCK_FLG,
	B_OTG_HICCUP_FLG,B_CHG_TIMEOUT_FLG,B_VPMID_SHORT_FLG,B_VPMID_OVP_OTG_FLG,
    
	B_CONV_OCP_MASK,B_VSYS_OVP_MASK,B_IBAT_OCP_MASK,B_VBAT_OVP_BUCK_MASK,
	B_OTG_HICCUP_MASK,B_CHG_TIMEOUT_MASK,B_VPMID_SHORT_MASK,B_VPMID_OVP_OTG_MASK,
	
	B_BIT_MAX,
};

typedef enum {
	ADC_IBUS = 0,
    ADC_VBUS,
	ADC_VAC,	
	ADC_VBATSNS,
    ADC_VBAT,
    ADC_IBAT,
    ADC_VSYS,
    ADC_TSBUS,
	ADC_TSBAT,
    ADC_TDIE,
    ADC_MAX_NUM,
} SGM41620_ADC_CH;

struct sgm41620_state{
    int ibus_adc;
    int vbus_adc;
    int vbatsns_adc;
    int vbat_adc;
    int ibat_adc;
    int vsys_adc;
    int tsbus_adc;
    int tsbat_adc;
    int tdie_adc;
};

/*
static const char* sgm41620_adc_name[ADC_MAX_NUM] = {
    [ADC_IBUS] = "ibus_adc",
    [ADC_VBUS] = "vbus_adc",
    [ADC_VAC] = "vac_adc",
    [ADC_VBATSNS] = "vbatsns_adc",
    [ADC_VBAT] = "vbat_adc",
    [ADC_IBAT] = "ibat_adc",
    [ADC_VSYS] = "vsys_adc",
    [ADC_TSBUS] = "tsbus_adc",
    [ADC_TSBAT] = "tsbat_adc",
    [ADC_TDIE] = "tdie_adc",
};
*/

 int sgm41620_charger_read_reg(uint8_t reg, int len, uint8_t *dst)
{	
	int ret = 0, count = 5;
	uint8_t buffer = 0;
	
	while (count) {
		if (len > 1) {			
			ret = _i2c1_rcv(SGM41620_CHARGER_DEV_ADDR, reg,dst,len);
			if (ret != 0)
				count--;
			else
				return ret;
		} else {
			ret = _i2c1_rcv(SGM41620_CHARGER_DEV_ADDR, reg,&buffer,1);
			if (ret != 0)
				count--;
			else {
				*(uint8_t *)dst = buffer;
				return ret;
			}
		}
		delay_1us(100);
	}
	return ret;
}

int sgm41620_charger_write_reg(uint8_t reg, int len, uint8_t *src)
{		
	int ret = 0, count = 5;

	while (count) {
		if (len > 1) {            
			ret = _i2c1_send(SGM41620_CHARGER_DEV_ADDR,reg, src, len);
			if (ret != 0)
				count--;
			else
				return ret;
		} else {			
			ret = _i2c1_send(SGM41620_CHARGER_DEV_ADDR,reg, src, 1);
			if (ret != 0)
				count--;
			else
				return ret;
		}
		delay_1us(100);
	}
	return ret;
}


 int sgm41620_trim_read_reg(uint8_t reg, int len, uint8_t *dst)
{	
	int ret = 0, count = 5;
	uint8_t buffer = 0;
	
	while (count) {
		if (len > 1) {			
			ret = _i2c1_rcv(SGM41620_CHARGER_TRIM_ADDR, reg,dst,len);
			if (ret != 0)
				count--;
			else
				return ret;
		} else {
			ret = _i2c1_rcv(SGM41620_CHARGER_TRIM_ADDR, reg,&buffer,1);
			if (ret != 0)
				count--;
			else {
				*(uint8_t *)dst = buffer;
				return ret;
			}
		}
		delay_1us(100);
	}
	return ret;
}

//temp for physical test
int sgm41620_trim_write_reg(uint8_t reg, int len, uint8_t *src)
{		
	int ret = 0, count = 5;

	while (count) {
		if (len > 1) {            
			ret = _i2c1_send(SGM41620_CHARGER_TRIM_ADDR,reg, src, len);							
			if (ret != 0)
				count--;
			else
				return ret;
		} else {			
			ret = _i2c1_send(SGM41620_CHARGER_TRIM_ADDR,reg, src, 1);
			if (ret != 0)
				count--;
			else
				return ret;
		}
		delay_1us(100);
	}
	return ret;
}

static inline int sgm41620_trim_i2c_write16(uint8_t reg, const uint16_t data)
{	
    int ret;
	ret = sgm41620_trim_write_reg(reg, 2, (uint8_t *)&data);
    if (ret) {
		log_e("i2c write16 Failed: reg=%02X, ret=%d\n", reg, ret);
		return ret;
	}
	return 0;
}

static inline int sgm41620_trim_i2c_write8(uint8_t reg, uint8_t data)
{
    int ret;
	ret = sgm41620_trim_write_reg(reg, 1, &data);
    if (ret) {
		log_e("i2c write8 Failed: reg=%02X, ret=%d\n", reg, ret);
		return ret;
	}
	return 0;
}

static inline int sgm41620_charger_i2c_write8(uint8_t reg, uint8_t data)
{
    int ret;
	ret = sgm41620_charger_write_reg(reg, 1, &data);
    if (ret) {
		log_e("i2c write8 Failed: reg=%02X, ret=%d\n", reg, ret);
		return ret;
	}
	return 0;
}

static inline int __attribute__((unused)) sgm41620_charger_i2c_write16(uint8_t reg, const uint16_t data)
{	
    int ret;
	ret = sgm41620_charger_write_reg(reg, 2, (uint8_t *)&data);
    if (ret) {
		log_e("i2c write16 Failed: reg=%02X, ret=%d\n", reg, ret);
		return ret;
	}
	return 0;
}

static inline int sgm41620_charger_i2c_read8(uint8_t reg,uint8_t *data)
{
    int ret;
	ret = sgm41620_charger_read_reg(reg,1,data);
    if (ret) {
		log_e("i2c read8 Failed: reg=%02X, ret=%d\n", reg, ret);
		return ret;
	}
	return 0;
}

static inline int sgm41620_trim_i2c_read8(uint8_t reg,uint8_t *data)
{
    int ret;
	ret = sgm41620_trim_read_reg(reg,1,data);
    if (ret) {
		log_e("i2c read8 Failed: reg=%02X, ret=%d\n", reg, ret);
		return ret;
	}
	return 0;
}


static inline int __attribute__((unused))
sgm41620_charger_i2c_read16(uint8_t reg,uint16_t *data)
{
	int ret;

	ret = sgm41620_charger_read_reg(reg,2,(uint8_t *)data);
	if (ret) {
		log_e("i2c read16 Failed: reg=%02X, ret=%d\n", reg, ret);
		return ret;
	}
	return 0;
}

int sgm41620_charger_update_bits(uint8_t reg,uint8_t mask, uint8_t val)
{
	uint8_t tmp;
	int ret;
	
    ret = sgm41620_charger_i2c_read8(reg, &tmp);	
	if (ret) {		
		goto out;
	}

	tmp &= ~mask;
	tmp |= val & mask;
	
    ret = sgm41620_charger_i2c_write8(reg, tmp);
out:	
	return ret;
}

int sgm41620_charger_hw_init(void)
{
    uint8_t data[1];
    int ret = 0;
   
    ret = sgm41620_charger_i2c_read8(0x00,data);
    if (data[0] != 0x95)
    {
        log_e("Don't found charge device  data[0] = %d ret = %d\n",data[0],ret);
        return 0;
    }
    sgm41620_charger_i2c_write8(0x7,0x0); // WDT Disable
#if SG41620_C_PATCH
    sgm41620_charger_i2c_write8(0xF,0x80); //ADC Enable
#endif
    sgm41620_charger_i2c_write8(0x30,0x4); //VSYS_MIN 3.4V
    sgm41620_charger_i2c_write8(0x31,0x30); //VBAT_REG  4232V
    sgm41620_charger_i2c_write8(0x32,0xA); //ICHG  500mA
    sgm41620_charger_i2c_write8(0x3c,0x1); //ICHG  500mA
    
    sgm41620_charger_update_bits(0x39,0x7,4);//boost current 2.1A
    //sgm41620_charger_update_bits(0x7,0x7,0);// WDT Disable-
    //sgm41620_charger_update_bits(0x3c,0x1,0x1);// enable charge
	
	sgm41620_trim_i2c_write16(0xEE,0x6241);//test mode
	sgm41620_trim_i2c_write8(0xdb,0x1e); // trim phy

	ret = sgm41620_trim_i2c_read8(0xdb,data);

	log_i("sgm41620_charger_hw_init trimdata = %x\n",data[0]);
	return ret;
}

int sgm41620_chg_work_handle()
{
    return 0;
}

int sgm41620_get_adc_data(int channel, int *result)
{
    uint8_t val[2] = {0};
    int ret;
	//int step = 0;
	uint16_t temp = 0;
    if(channel >= ADC_MAX_NUM) 
        return -1;

    ret = sgm41620_charger_read_reg(0x11 + (channel << 1),2, val);
    if (ret < 0) {
        return ret;
    }
    
    temp = val[1] | (val[0] << 8);
	switch(channel)
	{
		case ADC_IBUS:
            *result = (temp & 0xFFF) * 2500 /1000;
		case ADC_VBUS:
            *result = (temp & 0xFFF) * 3750 /1000;
		case ADC_VAC:
            *result = (temp & 0xFFF) * 5000 /1000;
		case ADC_VBATSNS:
        case ADC_VBAT:
		case ADC_IBAT:
        case ADC_VSYS:
            *result = (temp & 0xFFF) * 1250 /1000;
			break;
		case ADC_TSBUS:
		case ADC_TSBAT:			
			*result = (temp & 0x3FF) * 9766 / 10000;
			break;
		case ADC_TDIE:
			*result = (temp & 0x1FF) * 5 / 10;
			break;
		
		default:
			break;
	}
	
    //log_i("%s temp = 0x%x[%d]\r\n", sgm41620_adc_name[channel], temp,*result);
    return 0;
}

int sgm41620_disable_hiz(void)
{
    int ret = 0;

	ret = sgm41620_charger_update_bits(
                        0x3c,
                        1<<7,
                        0);

    if (ret) {
        log_e("sgm41620_disable_hiz!\n");
        return ret;
    }

    return ret;
}

int sgm41620_enable_vbus(void)
{
    int ret = 0;

	ret = sgm41620_charger_update_bits(0x07,SGM41620_ACDRV_EN | SGM41620_ACDRV_MANUAL_EN,
									SGM41620_ACDRV_EN | SGM41620_ACDRV_MANUAL_EN);
	ret += sgm41620_charger_update_bits(0x3C,SGM41620_BOOST_EN | SGM41620_QB_EN|SGM41620_HIZ_EN,
									SGM41620_BOOST_EN | SGM41620_QB_EN);

    if (ret) {
        log_e("set OTG enable error!\n");
        return ret;
    }

    return ret;
}

int sgm41620_disable_vbus(void)
{
    int ret = 0;

	ret = sgm41620_charger_update_bits(0x07,SGM41620_ACDRV_EN | SGM41620_ACDRV_MANUAL_EN,0);
	ret += sgm41620_charger_update_bits(0x3C,SGM41620_BOOST_EN | SGM41620_QB_EN | SGM41620_HIZ_EN | SGM41620_CHRG_EN,
									SGM41620_HIZ_EN);

    if (ret) {
        log_e("set OTG enable error!\n");
        return ret;
    }

    return ret;
}

#endif // #if CHIP_TYPE == CHIP_SG41620

